package algorithm.combination;

import java.util.*;
import java.util.function.Consumer;


public class CombinationNonRecursive {
    int[] dataArray;

    public CombinationNonRecursive(int[] dataArray) {
        this.dataArray = dataArray;
    }

    public void visit(int expectLength, Consumer<int[]> onVisit) {
        Stack<ActionEvent> stack = new Stack<>();
        int[] currentResult = new int[expectLength];
        stack.push(new ActionEvent("visit", Map.of("toVisitIndex", 0, "currentIndex", 0)));
        while (!stack.empty()) {
            ActionEvent event = stack.pop();
            switch (event.eventType) {
                case "visit" -> {
                    Map<?, ?> data = (Map<?, ?>) event.data();
                    int toVisitIndex = (int) data.get("toVisitIndex");
                    int currentIndex = (int) data.get("currentIndex");
                    List<ActionEvent> events = new ArrayList<>();
                    if (currentIndex == expectLength) {
                        events.add(createEvent("onVisit", null));
                    } else if (toVisitIndex < dataArray.length) {
                        events.add(createEvent("prepareData", Map.of("toVisitIndex", toVisitIndex, "currentIndex", currentIndex)));
                        events.add(createEvent("visit", Map.of("toVisitIndex", toVisitIndex + 1,
                                "currentIndex", currentIndex + 1)));
                        events.add(createEvent("visit", Map.of("toVisitIndex", toVisitIndex + 1,
                                "currentIndex", currentIndex)));
                    }
                    for (int i = 0; i < events.size(); i++) {
                        stack.push(events.get(events.size() - 1 - i));
                    }
                }
                case "prepareData" -> {
                    Map<?, ?> data = (Map<?, ?>) event.data();
                    int toVisitIndex = (int) data.get("toVisitIndex");
                    int currentIndex = (int) data.get("currentIndex");
                    currentResult[currentIndex] = dataArray[toVisitIndex];
                }
                case "onVisit" -> {
                    onVisit.accept(currentResult);
                }
            }
        }
    }

    private record ActionEvent(String eventType, Object data) {
    }

    ActionEvent createEvent(String eventType, Object data) {
        return new ActionEvent(eventType, data);
    }
}
